package io.github.echarts.model.geometry;

/**
 * 表示二维平面上的圆形
 * 使用逻辑坐标系（左下角为原点，Y轴向上为正方向）
 */
public class Circle {
    
    /**
     * 圆心
     */
    private final Point center;
    
    /**
     * 半径
     */
    private final double radius;
    
    /**
     * 创建圆形
     * 
     * @param center 圆心
     * @param radius 半径
     */
    public Circle(Point center, double radius) {
        if (center == null) {
            throw new IllegalArgumentException("圆心不能为null");
        }
        if (radius < 0) {
            throw new IllegalArgumentException("半径必须为非负数");
        }
        
        this.center = center;
        this.radius = radius;
    }
    
    /**
     * 创建圆形
     * 
     * @param centerX 圆心X坐标
     * @param centerY 圆心Y坐标
     * @param radius 半径
     */
    public Circle(double centerX, double centerY, double radius) {
        this(new Point(centerX, centerY), radius);
    }
    
    /**
     * 获取圆心
     * 
     * @return 圆心
     */
    public Point getCenter() {
        return center;
    }
    
    /**
     * 获取半径
     * 
     * @return 半径
     */
    public double getRadius() {
        return radius;
    }
    
    /**
     * 获取圆的直径
     * 
     * @return 直径
     */
    public double getDiameter() {
        return radius * 2;
    }
    
    /**
     * 获取圆的周长
     * 
     * @return 周长
     */
    public double getCircumference() {
        return 2 * Math.PI * radius;
    }
    
    /**
     * 获取圆的面积
     * 
     * @return 面积
     */
    public double getArea() {
        return Math.PI * radius * radius;
    }
    
    /**
     * 判断点是否在圆内或圆上
     * 
     * @param point 要检查的点
     * @return 如果点在圆内或圆上，返回true，否则返回false
     */
    public boolean contains(Point point) {
        return center.distanceTo(point) <= radius;
    }
    
    /**
     * 判断点是否在圆上（考虑一定的误差）
     * 
     * @param point 要检查的点
     * @param epsilon 允许的误差
     * @return 如果点在圆上，返回true，否则返回false
     */
    public boolean isOnCircle(Point point, double epsilon) {
        double distance = center.distanceTo(point);
        return Math.abs(distance - radius) < epsilon;
    }
    
    /**
     * 判断点是否在圆上（使用默认误差1e-10）
     * 
     * @param point 要检查的点
     * @return 如果点在圆上，返回true，否则返回false
     */
    public boolean isOnCircle(Point point) {
        return isOnCircle(point, 1e-10);
    }
    
    /**
     * 计算圆与线段的交点
     * 
     * @param line 线段
     * @return 交点数组，可能包含0个、1个或2个交点
     */
    public Point[] getIntersectionPoints(Line line) {
        // 将问题转换为求解方程组：
        // 线段参数方程: P = P1 + t(P2 - P1)，其中 0 <= t <= 1
        // 圆方程: |P - C|^2 = r^2
        
        Point p1 = line.getStart();
        Point p2 = line.getEnd();
        
        double x1 = p1.getX();
        double y1 = p1.getY();
        double x2 = p2.getX();
        double y2 = p2.getY();
        
        double cx = center.getX();
        double cy = center.getY();
        
        double dx = x2 - x1;
        double dy = y2 - y1;
        
        // 构造二次方程：at^2 + bt + c = 0
        double a = dx * dx + dy * dy;
        double b = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
        double c = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy) - radius * radius;
        
        // 计算判别式
        double discriminant = b * b - 4 * a * c;
        
        if (discriminant < 0) {
            // 没有交点
            return new Point[0];
        } else if (Math.abs(discriminant) < 1e-10) {
            // 一个交点（切点）
            double t = -b / (2 * a);
            
            // 检查t是否在[0,1]范围内（即交点是否在线段上）
            if (t < 0 || t > 1) {
                return new Point[0];
            }
            
            double x = x1 + t * dx;
            double y = y1 + t * dy;
            return new Point[] { new Point(x, y) };
        } else {
            // 两个交点
            double t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
            double t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
            
            // 确定哪些交点在线段上
            boolean t1OnSegment = t1 >= 0 && t1 <= 1;
            boolean t2OnSegment = t2 >= 0 && t2 <= 1;
            
            if (t1OnSegment && t2OnSegment) {
                double x1i = x1 + t1 * dx;
                double y1i = y1 + t1 * dy;
                double x2i = x1 + t2 * dx;
                double y2i = y1 + t2 * dy;
                return new Point[] { new Point(x1i, y1i), new Point(x2i, y2i) };
            } else if (t1OnSegment) {
                double xi = x1 + t1 * dx;
                double yi = y1 + t1 * dy;
                return new Point[] { new Point(xi, yi) };
            } else if (t2OnSegment) {
                double xi = x1 + t2 * dx;
                double yi = y1 + t2 * dy;
                return new Point[] { new Point(xi, yi) };
            } else {
                return new Point[0];
            }
        }
    }
    
    /**
     * 判断此圆是否与另一个圆相交
     * 
     * @param other 另一个圆
     * @return 如果两圆相交或相切，返回true，否则返回false
     */
    public boolean intersects(Circle other) {
        // 两圆相交当且仅当两圆心距离小于等于两圆半径之和且大于等于两圆半径之差
        double distance = center.distanceTo(other.center);
        return distance <= radius + other.radius && distance >= Math.abs(radius - other.radius);
    }
    
    /**
     * 判断此圆是否包含另一个圆
     * 
     * @param other 另一个圆
     * @return 如果此圆完全包含另一个圆，返回true，否则返回false
     */
    public boolean contains(Circle other) {
        // 一个圆包含另一个圆当且仅当两圆心距离加上被包含圆的半径小于等于包含圆的半径
        double distance = center.distanceTo(other.center);
        return distance + other.radius <= radius;
    }
    
    /**
     * 获取圆的外接正方形（以圆心为中心）
     * 
     * @return 外接正方形
     */
    public Rectangle getBoundingSquare() {
        double halfSide = radius;
        return Rectangle.fromCenter(center, Dimension.square(2 * halfSide));
    }
    
    /**
     * 获取圆的外接矩形（以圆心为中心）
     * 
     * @return 外接矩形
     */
    public Rectangle getBoundingRectangle() {
        return Rectangle.fromCenter(center, new Dimension(radius * 2, radius * 2));
    }
    
    /**
     * 创建一个新的圆形，表示当前圆形偏移指定距离后的结果
     * 
     * @param deltaX X轴偏移量
     * @param deltaY Y轴偏移量
     * @return 偏移后的新圆形
     */
    public Circle offset(double deltaX, double deltaY) {
        Point newCenter = new Point(center.getX() + deltaX, center.getY() + deltaY);
        return new Circle(newCenter, radius);
    }
    
    /**
     * 创建一个新的圆形，表示当前圆形缩放指定比例后的结果
     * 
     * @param scale 缩放比例
     * @return 缩放后的新圆形
     */
    public Circle scale(double scale) {
        return new Circle(center, radius * scale);
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        
        Circle circle = (Circle) o;
        
        if (Double.compare(circle.radius, radius) != 0) return false;
        return center.equals(circle.center);
    }
    
    @Override
    public int hashCode() {
        int result;
        long temp;
        result = center.hashCode();
        temp = Double.doubleToLongBits(radius);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
    
    @Override
    public String toString() {
        return "Circle{" +
                "center=" + center +
                ", radius=" + radius +
                '}';
    }
} 